package com.xu.security.token;

import com.xu.security.constant.Constants;
import com.xu.security.model.OnlineUser;
import com.xu.security.prop.SecurityProperties;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author: 许乾伟
 * @Date: 2024/3/22 16:48
 */
@Service
public class TokenService {

    @Resource
    SecurityProperties properties;
    @Resource(name = "securityRedisTemplate")
    private RedisTemplate redisTemplate;
    /**
     * 创建token
     *
     * @param onlineUser
     * @return token
     */
    public String createToken(OnlineUser onlineUser) {
        //判断该账号是否已经登录
        String accountToken = (String)redisTemplate.opsForValue().get(getPrincipalKey(onlineUser.getUsername()));
        if (accountToken != null) {
            //注销掉以往的token
            redisTemplate.delete(getTokenKey(accountToken));
        }
        String token = UUID.randomUUID().toString();
        onlineUser.setToken(token);
        refreshToken(onlineUser);
        return token;
    }

    /**
     * 校验token有效性
     * @param token
     */
    public boolean verifyToken(String token) {
        return redisTemplate.hasKey(getTokenKey(token));
    }

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    public OnlineUser getOnlineUserByToken(String token) {
        return (OnlineUser)redisTemplate.opsForValue().get(getTokenKey(token));
    }

    /**
     * 获取当前用户
     * @param request
     * @return 用户信息
     */
    public OnlineUser getOnlineUser(HttpServletRequest request) {
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        return readToken(token);
    }

    /**
     * 删除用户token
     * @param token
     */
    public void delLoginUser(String token) {
        if (redisTemplate.hasKey(getTokenKey(token))) {
            OnlineUser onlineUser = (OnlineUser) redisTemplate.opsForValue().get(getTokenKey(token));
            if (onlineUser != null) {
                redisTemplate.delete(getPrincipalKey(onlineUser.getUsername()));
            }
            redisTemplate.delete(getTokenKey(token));
        }
    }

    /**
     * 存储token
     *
     * @param onlineUser
     */
    private void refreshToken(OnlineUser onlineUser) {
        long currentTime = System.currentTimeMillis();
        if (onlineUser.getLoginTime() == null) {
            onlineUser.setLoginTime(currentTime);
        }
        onlineUser.setRefreshTime(currentTime);
        onlineUser.setExpireTime(currentTime + properties.getExpireTime() * Constants.MILLIS_MINUTE);
        //根据token缓存用户信息
        redisTemplate.opsForValue().set(getTokenKey(onlineUser.getToken()), onlineUser, properties.getExpireTime(), TimeUnit.MINUTES);
        //根据账号缓存用户信息
        redisTemplate.opsForValue().set(getPrincipalKey(onlineUser.getUsername()), onlineUser.getToken(), properties.getExpireTime(), TimeUnit.MINUTES);
    }

    /**
     * 获取token
     *
     * @param request
     * @return
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(Constants.TOKEN_AUTH_HEADER);
        if (StringUtils.hasText(token) && token.startsWith(Constants.BEARER_TYPE)) {
            token = token.replace(Constants.BEARER_TYPE, "");
        }
        return token;
    }

    /**
     * 从redis中读取用户信息
     *
     * @param token
     * @return
     */
    private OnlineUser readToken(String token) {
        if (redisTemplate.hasKey(getTokenKey(token))){
            return (OnlineUser) redisTemplate.opsForValue().get(getTokenKey(token));
        }
        throw new RuntimeException("token已失效");

    }

    /**
     * 返回token的key
     *
     * @param uuid
     * @return
     */
    private String getTokenKey(String uuid) {
        return Constants.LOGIN_TOKEN_KEY + uuid;
    }

    /**
     * 返回凭证的key
     *
     * @param principal
     * @return
     */
    private String getPrincipalKey(String principal) {
        return Constants.LOGIN_ACCOUNT_KEY + principal;
    }

}
